package com.dianrong.common.uniauth.client.support;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import com.dianrong.common.uniauth.common.util.ReflectionUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PatternMatchMost {
	private static final Pattern EXCLUDE_CHARS_PATTERN = Pattern.compile("[(\\)(\\|)(\\,)(\\[)(\\])(\\{)(\\})(\\()(\\))(\\^)(\\$)(\\.)(\\-)(\\&)(\\?)(\\*)(\\+)(\\\\s)(\\\\S)(\\\\d)(\\\\D)(\\\\w)(\\\\W)]");
	private PatternMatchMost(){
		
	}
	
	//not perfect
	public static RequestMatcher findMachMostRequestMatcher(HttpServletRequest request, Map<RequestMatcher, Collection<ConfigAttribute>> allMatchedMap){
		String url = ExtractRequestUrl.extractRequestUrl(request);
		int questionPos = url.indexOf('?');
		
		RequestMatcher matchMostRequestMatcher = null;
		RequestMatcher matchedAnyRequestMatcher = null;
		int matchLength = -1;
		int originLength = -1;
		
		Iterator<Entry<RequestMatcher, Collection<ConfigAttribute>>> allMatchedIterator = allMatchedMap.entrySet().iterator();
		while(allMatchedIterator.hasNext()){
			Entry<RequestMatcher, Collection<ConfigAttribute>> matchedEntry = allMatchedIterator.next();
			RequestMatcher requestMatcher = matchedEntry.getKey();
			if(requestMatcher instanceof AntPathRequestMatcher){
				AntPathRequestMatcher antPathRequestMatcher = (AntPathRequestMatcher)requestMatcher;
				String pattern = antPathRequestMatcher.getPattern();
				
				//ant pattern exclude the ? and querystring parts when checking request url
				if(pattern.equals(url.subSequence(0, questionPos == -1 ? url.length() : questionPos))){
					matchMostRequestMatcher = antPathRequestMatcher;
					break;
				}
				
				int baseOriginLength = pattern.length();
				String baseOfPattern = EXCLUDE_CHARS_PATTERN.matcher(pattern).replaceAll("");
				int length = baseOfPattern.length();
				
				if(length == matchLength){
					if(baseOriginLength < originLength){
						matchMostRequestMatcher = antPathRequestMatcher;
						originLength = baseOriginLength;
					}
				}
				else if(length > matchLength){
					matchMostRequestMatcher = antPathRequestMatcher;
					matchLength = length;
					originLength = baseOriginLength;
				}
			}
			else if(requestMatcher instanceof RegexRequestMatcher){
				RegexRequestMatcher regexRequestMatcher = (RegexRequestMatcher)requestMatcher;
				String pattern = ((Pattern)(ReflectionUtils.getField(regexRequestMatcher, "pattern", false))).pattern();
				
				if(pattern.equals(url)){
					matchMostRequestMatcher = regexRequestMatcher;
					break;
				}
				
				int baseOriginLength = pattern.length();
				// 过滤掉正则表达式的模糊匹配字符串，比如/w,*,.,+这些字符串,把pattern过滤成一个正则无关的字符串
				String baseOfPattern = EXCLUDE_CHARS_PATTERN.matcher(pattern).replaceAll("");
				int length = baseOfPattern.length();

				if(length == matchLength){
					if(baseOriginLength < originLength){
						matchMostRequestMatcher = regexRequestMatcher;
						originLength = baseOriginLength;
					}
				}
				else if(length > matchLength){
					matchMostRequestMatcher = regexRequestMatcher;
					matchLength = length;
					originLength = baseOriginLength;
				}
			}
			else if(requestMatcher instanceof AnyRequestMatcher){
				matchedAnyRequestMatcher = (AnyRequestMatcher)requestMatcher;
			}
		}
		matchMostRequestMatcher = matchMostRequestMatcher == null? matchedAnyRequestMatcher : matchMostRequestMatcher;
		
		int mapSize = allMatchedMap.size();
		
		if(mapSize >= 1){
			if(log.isWarnEnabled()){
				String pattern = "";
				if(matchMostRequestMatcher instanceof RegexRequestMatcher){
					pattern = ((Pattern)(ReflectionUtils.getField(matchMostRequestMatcher, "pattern", false))).pattern();
					pattern = "(" + pattern + ")";
				}
				log.warn("Found " + mapSize + " patterns <" + allMatchedMap + "> matching <" + url +">, choose <" + matchMostRequestMatcher + pattern + ">");
			}
		}
		
		return matchMostRequestMatcher;
	}
}
